package mail;

import java.io.IOException;
import java.util.Date;
import java.util.LinkedList;
import java.util.Properties;
import java.util.Scanner;

import javax.mail.Address;
import javax.mail.Authenticator;
import javax.mail.FetchProfile;
import javax.mail.Flags;
import javax.mail.Folder;
import javax.mail.FolderClosedException;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Multipart;
import javax.mail.NoSuchProviderException;
import javax.mail.PasswordAuthentication;
import javax.mail.Session;
import javax.mail.Store;
import javax.mail.Transport;
import javax.mail.event.MessageCountAdapter;
import javax.mail.event.MessageCountEvent;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;

import tools.OptionReader;

import com.sun.mail.imap.IMAPFolder;

/**
 * Implementierung des ModelEmail Interface
 * 
 * @author Michi
 * 
 */
public class MyModelEmail implements interfaces.ModelEmail {
	Folder inbox;
	private MimeMessageReader mmr;
	private String[] mailOptions;

	public String[] getMailOptions() {
		return mailOptions;
	}

	public void setMailOptions(String[] mailOptions) {
		this.mailOptions = mailOptions;
	}

	public MimeMessageReader getMmr() {
		return mmr;
	}

	public void setMmr(MimeMessageReader mmr) {
		this.mmr = mmr;
	}

	public Folder getInbox() {
		return inbox;
	}

	public void setInbox(Folder inbox) {
		this.inbox = inbox;
	}

	/**
	 * Listinbox wird in regelmaessigen Abstaenden aufgerufen um Emails zu
	 * empfangen
	 */
	public void updateInbox() {
		Properties props = new Properties();

		OptionReader or = new OptionReader();
		try {
			or.getOpionsFromFile();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		String mailOptionsTemp = or.getMailOpt1();
		setMailOptions(mailOptionsTemp.split(","));

		String host = mailOptions[1];
		String username = mailOptions[0];
		String password = mailOptions[7];
		String provider = mailOptions[6];
		String time = "2000";

		try {

			// Connect to the server
			Session session = Session.getDefaultInstance(props, null);

			Store store = session.getStore(provider);

			store.connect(host, username, password);

			// open the inbox folder
			inbox = store.getFolder("INBOX");

			inbox.open(Folder.READ_WRITE);

			// Add messageCountListener to listen for new messages
			inbox.addMessageCountListener(new MessageCountAdapter() {
				public void messagesAdded(MessageCountEvent ev) {

				}
			});

			// Check mail once in "freq" MILLIseconds
			int freq = Integer.parseInt(time);
			boolean supportsIdle = false;
			try {
				if (inbox instanceof IMAPFolder) {
					IMAPFolder f = (IMAPFolder) inbox;
					f.idle();
					supportsIdle = true;
				}
			} catch (FolderClosedException fex) {
				throw fex;
			} catch (MessagingException mex) {
				supportsIdle = false;
			}
			for (;;) {
				if (supportsIdle && inbox instanceof IMAPFolder) {
					IMAPFolder f = (IMAPFolder) inbox;
					f.idle();

				} else {
					Thread.sleep(freq); // sleep for freq milliseconds

					// This is to force the IMAP server to send us
					// EXISTS notifications.
					inbox.getMessageCount();
				}
			}

		} catch (Exception ex) {
			ex.printStackTrace();
		}

	}

	/**
	 * Oeffnet das "Neue Email" Fenster
	 */
	public void newMail() {
		new gui.NewMailWindow();

	}

	/**
	 * Methode sendet eine Email ab
	 * 
	 * @param owner
	 *            Besitzer des Postfachs
	 * @param mail
	 *            Email die gesendet werden soll
	 */
	public void sendMail(Mail mail, Boolean b) {

		OptionReader or = new OptionReader();
		try {
			or.getOpionsFromFile();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		String mailOptionsTemp = or.getMailOpt1();
		setMailOptions(mailOptionsTemp.split(","));

		// Properties der verbindung setzen
		Properties props = new Properties();
		props.put("mail.smtp.host", mailOptions[3]);
		props.put("mail.from", mailOptions[0]);
		props.put("mail.smtp.auth", "true");
		props.put("mail.smtp.port", mailOptions[8]);

		if (mailOptions[3].contains("gmail")
				|| mailOptions[3].contains("googlemail")) {
			props.put("mail.smtp.starttls.enable", true);
		}

		// Verbindungsaufbau und Authentifizierung erstellen
		Session session = Session.getInstance(props, new Authenticator() {
			public PasswordAuthentication getPasswordAuthentication() {
				String username = mailOptions[0];
				String password = mailOptions[7];

				return new PasswordAuthentication(username, password);

			}
		});

		try {
			MimeMessage msg = new MimeMessage(session);
			msg.setFrom();

			// Address[] adbcc = null;
			Address[] adcc = null;

			Address[] ad = new Address[mail.getTo().size()];
			for (int i = 0; i < mail.getTo().size(); i++) {
				InternetAddress temp = new InternetAddress();
				temp.setAddress(mail.getTo().get(i));
				ad[i] = temp;
			}

			if (!mail.getCc().isEmpty()) {
				adcc = new Address[mail.getCc().size()];
				for (int i = 0; i < mail.getCc().size(); i++) {
					if (mail.getCc().get(i) != null) {
						InternetAddress temp = new InternetAddress();
						temp.setAddress(mail.getCc().get(i));

						adcc[i] = temp;
					} else {
						adcc = null;
					}
				}
			}

			// if (!mail.getBcc().isEmpty()) {
			// adbcc = new Address[mail.getBcc().size()];
			// for (int i = 0; i < mail.getBcc().size(); i++) {
			// if (mail.getCc().get(i) != null) {
			// InternetAddress temp = new InternetAddress();
			// temp.setAddress(mail.getBcc().get(i));
			// adbcc[i] = temp;
			// } else {
			// adbcc = null;
			// }
			// }
			// }

			msg.setRecipients(Message.RecipientType.TO, ad);
			if (!mail.getCc().isEmpty() && adcc != null) {
				msg.setRecipients(Message.RecipientType.CC, adcc);
			}

			// if (!mail.getBcc().isEmpty() && adbcc != null) {
			// msg.setRecipients(Message.RecipientType.BCC, adbcc);
			// }
			if (b) {

				msg.setSubject(mail.getBetreff() + "  <==3");
			} else {
				msg.setSubject(mail.getBetreff());
			}

			msg.setSentDate(new Date());
			msg.setText(mail.getText());
			Transport.send(msg);
		} catch (MessagingException mex) {
			System.out.println("send failed, exception: " + mex);
		}

	}

	/**
	 * Empfaengt Emails, speichert diese als Objekt Mail in eine LinkedList
	 * 
	 * @param owner
	 *            Besitzer des Emailpostfachs
	 * @return LinkedList mit allen Emails des Servers
	 */
	public LinkedList<Mail> listInbox() {
		Properties props = new Properties();

		OptionReader or = new OptionReader();
		try {
			or.getOpionsFromFile();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		String mailOptionsTemp = or.getMailOpt1();
		setMailOptions(mailOptionsTemp.split(","));

		String host = mailOptions[1];
		String username = mailOptions[0];
		String password = mailOptions[7];
		String provider = mailOptions[6];

		try {

			// Connect to the server
			Session session = Session.getDefaultInstance(props, null);
			Store store = session.getStore(provider);
			store.connect(host, username, password);

			// open the inbox folder
			Folder inbox = store.getFolder("INBOX");

			inbox.open(Folder.READ_ONLY);

			// get a list of javamail messages as an array of messages
			Message[] messages = inbox.getMessages();

			LinkedList<Mail> ll = new LinkedList<Mail>();

			for (int i = 0; i < messages.length; i++) {

				mmr = new MimeMessageReader(messages[i]);
				// System.out.println(mmr.messageToString());

				Mail temp = new Mail();

				String from = getFrom(messages[i]);
				temp.setFrom(from);

				temp.setTo(getToList(messages[i]));

				if (mmr.getContent() != null) {
					temp.setText(mmr.getContent());
				}

				String betreff = messages[i].getSubject();
				temp.setBetreff(betreff);

				if (mmr.getTextType() != null) {

					if (mmr.getTextType().contains("TEXT/PLAIN")) {
						temp.setTextType("text/plain");
					}

					else if (mmr.getTextType().contains("TEXT/HTML")) {
						temp.setTextType("text/html");
					}

					else if (mmr.getTextType().contains("text/plain")) {
						temp.setTextType("text/plain");
					}

					else if (mmr.getTextType().contains("text/html")) {
						temp.setTextType("text/html");
					} else {
						temp.setTextType("text/html");
					}

				} else {
					temp.setTextType("text/html");
				}

				// String textType = messages[i].getContentType();
				// temp.setTextType(textType);

				Date sent = messages[i].getSentDate();
				temp.setSentDate(sent);

				int k = messages[i].getMessageNumber();
				temp.setMailNumber(k);

				int h = messages[i].hashCode();
				temp.setHash(h);

				if (from != null) {
					from = removeQuotes(from);
					ll.add(temp);

				}

				temp.setAttachments(mmr.getAttachments());
			}

			// close the inbox folder but do not
			// remove the messages from the server
			inbox.close(false);
			store.close();
			return ll;
		} catch (NoSuchProviderException nspe) {
			System.err.println("invalid provider name");
		} catch (MessagingException me) {
			System.err.println("messaging exception");
			me.printStackTrace();
		} catch (Exception e) {

			e.printStackTrace();
		}

		return null;

	}

	/**
	 * Funktion gibt Absender als String zurueck
	 * 
	 * @param javaMailMessage
	 *            Email die uebergebenw wird
	 * @return Email Absender als String
	 * @throws MessagingException
	 */
	private static String getFrom(Message javaMailMessage)
			throws MessagingException {
		String from = "";
		Address a[] = javaMailMessage.getFrom();
		if (a == null)
			return null;
		for (int i = 0; i < a.length; i++) {
			Address address = a[i];
			from = from + address.toString();
		}

		return from;
	}

	/**
	 * Gibt eine Linked List mit allen Empfaengern der Email zurueck
	 * 
	 * @param javaMailMessage
	 *            Email die uebergeben wird
	 * @return LinkedList vom Typ String mit allen Emailadressen der Emfaenger
	 * @throws MessagingException
	 */
	private static LinkedList<String> getToList(Message javaMailMessage)
			throws MessagingException {
		LinkedList<String> to = new LinkedList<String>();
		Address a[] = javaMailMessage.getAllRecipients();
		if (a == null)
			return null;
		for (int i = 0; i < a.length; i++) {
			Address address = a[i];
			String str = address.toString();

			String mailPattern = "[_A-Za-z0-9-\\+]+(\\.[_A-Za-z0-9-]+)*@[A-Za-z0-9-]+(\\.[A-Za-z0-9]+)*(\\.[A-Za-z]{2,})";
			Scanner scan = new Scanner(str);
			to.add(scan.findInLine(mailPattern));

		}

		return to;
	}

	/**
	 * Funktion entfernt Anfuehrungszeichen von Emailadressen
	 * 
	 * @param stringToModify
	 *            Emailadresse die modifiziert werden soll
	 * @return Uebergebener String ohne Anfuehrungseichen
	 */
	private static String removeQuotes(String stringToModify) {
		int indexOfFind = stringToModify.indexOf(stringToModify);
		if (indexOfFind < 0)
			return stringToModify;

		StringBuffer oldStringBuffer = new StringBuffer(stringToModify);
		StringBuffer newStringBuffer = new StringBuffer();
		for (int i = 0, length = oldStringBuffer.length(); i < length; i++) {
			char c = oldStringBuffer.charAt(i);
			if (c == '"' || c == '\'') {
				// do nothing
			} else {
				newStringBuffer.append(c);
			}

		}
		return new String(newStringBuffer);
	}

	/**
	 * Funktion loescht eine Email vom Server
	 * 
	 * @param owner
	 *            Besitzer des Emailpostfachs
	 * @param mail
	 *            Mail die geloescht werden soll
	 */
	public void deleteMail(Mail mail) {

		OptionReader or = new OptionReader();
		try {
			or.getOpionsFromFile();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		String mailOptionsTemp = or.getMailOpt1();
		setMailOptions(mailOptionsTemp.split(","));

		Properties props = new Properties();

		String host = mailOptions[1];
		String username = mailOptions[0];
		String password = mailOptions[7];
		String provider = mailOptions[6];

		try {

			// Connect to the server
			Session session = Session.getDefaultInstance(props, null);

			Store store = session.getStore(provider);

			store.connect(host, username, password);

			// open the inbox folder
			Folder inbox = store.getFolder("INBOX");

			inbox.open(Folder.READ_WRITE);

			// get a list of javamail messages as an array of messages
			Message[] messages = inbox.getMessages();

			FetchProfile fp = new FetchProfile();
			fp.add(FetchProfile.Item.ENVELOPE);
			fp.add(FetchProfile.Item.FLAGS);
			fp.add(FetchProfile.Item.CONTENT_INFO);
			fp.add("X-mailer");
			inbox.fetch(messages, fp);

			for (int i = 0; i < messages.length; i++) {

				if (compareMails(messages[i], mail)) {
					messages[i].setFlag(Flags.Flag.DELETED, true);

				}

			}

			inbox.close(true);

			store.close();

		}

		catch (NoSuchProviderException nspe) {
			System.err.println("invalid provider name");
		} catch (MessagingException me) {
			System.err.println("messaging exception");
			me.printStackTrace();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * Funktion oeffner "Neue Email" Fenster und setzt Adressat auf den
	 * Absender, uebernimmt Inhalt der Email und setzt den Betreff auf Re: +
	 * Betreff der uebergebenen Email der uebergebenen Mail
	 * 
	 * @param mail
	 *            Mail auf die geantwortet werden soll
	 */
	public void answerMail(Mail mail) {

		String strBetreff = "Re: " + mail.getBetreff();
		String strMailTo = mail.getFrom();
		String content = "\n" + "\n" + mail.getText();

		new gui.NewMailWindow(strMailTo, strBetreff, content).setVisible(true);

	}

	/**
	 * Funktion oeffner "Neue Email" Fenster und setzt Adressaten auf den
	 * Absender und alle Empfaenger der Email, uebernimmt Inhalt der Email und
	 * setzt den Betreff auf Re: + Betreff der uebergebenen Email der
	 * uebergebenen Mail
	 * 
	 * @param mail
	 *            Mail auf die geantwortet wird
	 */
	public void answerAllMail(Mail mail) {

		String strBetreff = "Re: " + mail.getBetreff();
		String strMailTo = mail.getFrom() + ", " + mail.getToString();
		String content = "\n" + "\n" + mail.getText();

		new gui.NewMailWindow(strMailTo, strBetreff, content).setVisible(true);

	}

	/**
	 * Leitet Email weiter. Es werden Betreff mit Praefix Fwd uebernommen und
	 * Inhalt der Email
	 * 
	 * @param mail
	 *            Email die weitergeleitet werden soll
	 */
	public void forwMail(Mail mail) {
		String strBetreff = "Fwd: " + mail.getBetreff();
		String content = "\n" + "\n" + mail.getText();

		new gui.NewMailWindow(strBetreff, content).setVisible(true);
	}

	/**
	 * Vergleicht Emails ob sie identisch sind
	 * 
	 * @param msg
	 *            Email auf dem Server
	 * @param mail
	 *            Email aus der Datenbank
	 * @return Boolean True, wenn Email identisch
	 */
	public boolean compareMails(Message msg, Mail mail) {
		boolean ret = false;
		String content = "";

		try {
			if (msg.getContent() instanceof Multipart) {

				Multipart mime = (Multipart) msg.getContent();
				for (int j = 0; j < mime.getCount(); j++) {

					content = mime.getBodyPart(0).getContent().toString();

				}
			} else {
				content = msg.getContent().toString();

			}
		} catch (IOException e) {

			e.printStackTrace();
		} catch (MessagingException e) {

			e.printStackTrace();
		}

		try {
			String mailServer = msg.getSubject() + msg.getSentDate().toString()
					+ content;

			String mailLocal = mail.getBetreff()
					+ mail.getSentDate().toString() + content;

			if (mailServer.equals(mailLocal)) {
				ret = true;

			}

		} catch (MessagingException e) {

			e.printStackTrace();
		}

		return ret;

	}

	/**
	 * Löscht alle Emails
	 * 
	 * @throws MessagingException
	 */
	public void deleteAllMails() throws MessagingException {

		OptionReader or = new OptionReader();
		try {
			or.getOpionsFromFile();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		String emailSettings = or.getMailOpt1();
		String[] mailSettings = emailSettings.split(",");

		Properties props = new Properties();
		String host = mailSettings[1];
		String username = mailSettings[0];
		String password = mailSettings[7];
		String provider = mailSettings[6];

		Session session = Session.getDefaultInstance(props, null);
		Store store = session.getStore(provider);
		store.connect(host, username, password);

		// open the inbox folder
		Folder inbox = store.getFolder("INBOX");

		inbox.open(Folder.READ_WRITE);

		// get a list of javamail messages as an array of messages
		Message[] messages = inbox.getMessages();

		for (int i = 0; i < messages.length; i++) {

			messages[i].setFlag(Flags.Flag.DELETED, true);

		}

		inbox.close(true);
		store.close();
	}

	/**
	 * Setzt alle Mails auf ungelesen
	 * 
	 * @throws MessagingException
	 */
	public void setAllMailsUnread() throws MessagingException {

		OptionReader or = new OptionReader();
		try {
			or.getOpionsFromFile();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		String emailSettings = or.getMailOpt1();
		String[] mailSettings = emailSettings.split(",");

		Properties props = new Properties();
		String host = mailSettings[1];
		String username = mailSettings[0];
		String password = mailSettings[7];
		String provider = mailSettings[6];

		Session session = Session.getDefaultInstance(props, null);
		Store store = session.getStore(provider);
		store.connect(host, username, password);

		// open the inbox folder
		Folder inbox = store.getFolder("INBOX");

		inbox.open(Folder.READ_WRITE);

		// get a list of javamail messages as an array of messages
		Message[] messages = inbox.getMessages();

		Flags flags = new Flags();
		flags.add(Flags.Flag.ANSWERED);
		flags.add(Flags.Flag.DELETED);
		flags.add(Flags.Flag.DRAFT);
		flags.add(Flags.Flag.FLAGGED);
		flags.add(Flags.Flag.SEEN);
		flags.add(Flags.Flag.USER);

		for (int i = 0; i < messages.length; i++) {

			messages[i].setFlags(flags, false);

		}

		inbox.close(true);
		store.close();
	}

}
